package com.xxkk.net.websocketNetty;

import com.ebo.synframework.nettybase.codec.ProtoDecoder;
import com.ebo.synframework.nettybase.dispatcher.CmdDispatcher;
import com.ebo.synframework.nettybase.dispatcher.ReqCmd;
import com.ebo.synframework.synroom.executor.SynRoom;
import com.ebo.synframework.synroom.executor.SynRoomTask;
import com.ebo.synframework.synroom.executor.pool.SynRoomThreadPool;
import com.google.protobuf.Descriptors;
import com.google.protobuf.GeneratedMessageV3;
import com.xxkk.gameCore.enums.ErrorCode;
import com.xxkk.gameCore.vo.BusinessError;
import com.xxkk.utils.BusinessPool;
import com.xxkk.gameCore.global.globalServer.ServerContext;
import org.apache.log4j.Logger;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.*;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketFrame;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.net.InetSocketAddress;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * @author xxkk
 * @version 1.0.0
 * @title BaseMsgHandler
 * @description ʕ•ᴥ•ʔ
 * @create 2023/11/13 16:44
 **/
public abstract class BaseMsgHandler extends SimpleChannelUpstreamHandler implements ApplicationContextAware {
    private final static Logger logger = Logger.getLogger(BaseMsgHandler.class);
    private ApplicationContext ac;
    protected SynRoomThreadPool synRoomThreadPool;
    @Autowired
    protected ServerContext serverContext;
    @Autowired
    private BusinessPool businessPool;// 定时任务使用
    private ProtoDecoder protoDecoder = new ProtoDecoder();
    private Executor executor = Executors.newCachedThreadPool();// 用来执行一些不需要限制请求频率的任务
    @Autowired
    private CmdDispatcher cmdDispatcher;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.ac = applicationContext;
    }

    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
        try {
            // 默认服务器完全启动可接收消息
            Player player = (Player) ctx.getAttachment();
            player.setLastReqTime(System.currentTimeMillis());// 刷新响应时间
            ReqCmd reqCmd;
            if (e.getMessage() instanceof WebSocketFrame) {
                WebSocketFrame webSocketFrame = (WebSocketFrame) e.getMessage();
                ChannelBuffer buffer = webSocketFrame.getBinaryData();
                reqCmd = this.protoDecoder.decode0(ctx, buffer);// 协议解码器解码
                this.executor.execute(() -> {
                    this.handleMsg(reqCmd, player);// 消息命令处理
                });
            } else {
                logger.info("收到的消息不是websocket帧信息#" + e.getMessage());
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

    private void handleMsg(ReqCmd reqCmd, Player player) {
        try {
            String serviceAndMethod = reqCmd.getServiceAndMethodStr();
            this.printReqInfo(reqCmd, player);
            if (player.getResp(serviceAndMethod) != null) {
                player.setRespAndNotify(serviceAndMethod, reqCmd.getMessage());
                return;// 响应消息结束
            } else {// TODO 加if判断，需要验证一下玩家是否登陆 排除一下登陆方法 xx == playerState.login
                SynRoomTask task = new SynRoomTask(player.getLocation()) {
                    @Override
                    public void processTask() {
                        try {
                            dispatch(reqCmd, serviceAndMethod, player);// 命令分发
                        } catch (Throwable e) {
                            logger.error("unknown exception event:" + serviceAndMethod + ",roleId:" + player.getRoleId()
                                    + " param:" + getParam(reqCmd.getMessage()));
                            e.printStackTrace();
                            player.respond(serviceAndMethod, new BusinessError(ErrorCode.ServerException).newBuilder(), reqCmd.getCmdIndex());
                        }
                    }
                };
                this.synRoomThreadPool.execute(task);
            }
        } catch (Exception e) { // 运行到此处为超出预期的错误
            e.printStackTrace();
        }
    }

    private void dispatch(ReqCmd reqCmd, String serviceAndMethod, Player player)throws Throwable {
            Object object = this.cmdDispatcher.dispatch(reqCmd, player);
            if (object != null) {// 如果命令分发后方法有返回值
//                logger.info("客户端回调:"+serviceAndMethod+":"+ object.toString());
                player.respond(serviceAndMethod, object, reqCmd.getCmdIndex());
            }
    }

    /**
     * 请求打点，目前缺少传参打印 先搁置（因为影响性能） TODO
     *
     * @param reqCmd
     * @param player
     */
    private void printReqInfo(ReqCmd reqCmd, Player player) {
        logger.info("call(" + reqCmd.getLength()
                + "):" + reqCmd.getServiceAndMethodStr()
                + ",roleId:" + player.getRoleId()
                + ",param:[]");
    }

    /**
     * 添加打印获取参数功能
     */
    private String getParam(GeneratedMessageV3 message) {
        List<Descriptors.FieldDescriptor> fields = message.getDescriptorForType().getFields();
        StringBuffer sb = new StringBuffer("[");
        for (Descriptors.FieldDescriptor field : fields) {
            Object value = message.getField(field);
            if (value instanceof List) {
                sb.append("{");
                @SuppressWarnings("unchecked")
                List<Object> list = (List<Object>) value;
                for (Object li : list) {
                    sb.append(li.toString()).append(",");
                }
                sb.append("},");
            } else {
                sb.append(value.toString()).append(",");
            }
        }
        sb.append("]");
        return sb.toString();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
        logger.error("netty meet an uncatch error ", e.getCause());
    }

//    @Override
//    public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
//        logger.info("xxkk netty server start success ");
//    }

    @Override
    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
        try {
            logger.info("people connecting ʕ•ᴥ•ʔ " + e.toString());
            Player player = new Player(ctx);
            ctx.setAttachment(player);
            player.setLocation(new SynRoom(player.getPlayerId()));// 这个很重要
            InetSocketAddress address = (InetSocketAddress) ctx.getChannel().getRemoteAddress();
            player.setLoginIp(address.getAddress().getHostAddress());
            player.setConnectionTime(System.currentTimeMillis());// 设置连接时间，定时任务刷新超时连接使用
            Player player1 = this.serverContext.addConnectingPlayer(player);
            if (player1 != null)
                player1.close();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

    @Override
    public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
        try {
            Player player = (Player) ctx.getAttachment();
            logger.info("people disConnecting " + e.toString());
            playerDisconnect(player);
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

    public abstract void playerDisconnect(Player player);

    public SynRoomThreadPool getSynRoomThreadPool() {
        return synRoomThreadPool;
    }

    public void setSynRoomThreadPool(SynRoomThreadPool synRoomThreadPool) {
        this.synRoomThreadPool = synRoomThreadPool;
    }
}
